/*-----------------------------------------------------------------------
 * ascii.c
 *	 PostgreSQL 的字符串到 ascii 转换例程。
 *
 *	 Portions Copyright (c) 1999-2022, PostgreSQL Global Development Group
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/ascii.c
 *
 *-----------------------------------------------------------------------
 */
#include "postgres.h"

#include "mb/pg_wchar.h"
#include "utils/ascii.h"
#include "utils/builtins.h"

static void pg_to_ascii(unsigned char *fc_src, unsigned char *fc_src_end,
						unsigned char *fc_dest, int fc_enc);
static text *fc_encode_to_ascii(text *fc_data, int fc_enc);


/* ----------
 * to_ascii
 * ----------
 */
static void pg_to_ascii(unsigned char *fc_src, unsigned char *fc_src_end, unsigned char *fc_dest, int fc_enc)
{
	unsigned char *fc_x;
	const unsigned char *fc_ascii;
	int			fc_range;

	/*
	 * 编码的相关起始
	 */
#define RANGE_128	128
#define RANGE_160	160

	if (fc_enc == PG_LATIN1)
	{
		/*
		 * ISO-8859-1 <范围：160 -- 255>
		 */
		fc_ascii = (const unsigned char *) "  cL Y  \"Ca  -R     'u .,      ?AAAAAAACEEEEIIII NOOOOOxOUUUUYTBaaaaaaaceeeeiiii nooooo/ouuuuyty";
		fc_range = RANGE_160;
	}
	else if (fc_enc == PG_LATIN2)
	{
		/*
		 * ISO-8859-2 <范围：160 -- 255>
		 */
		fc_ascii = (const unsigned char *) " A L LS \"SSTZ-ZZ a,l'ls ,sstz\"zzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTBraaaalccceeeeiiddnnoooo/ruuuuyt.";
		fc_range = RANGE_160;
	}
	else if (fc_enc == PG_LATIN9)
	{
		/*
		 * ISO-8859-15 <范围：160 -- 255>
		 */
		fc_ascii = (const unsigned char *) "  cL YS sCa  -R     Zu .z   EeY?AAAAAAACEEEEIIII NOOOOOxOUUUUYTBaaaaaaaceeeeiiii nooooo/ouuuuyty";
		fc_range = RANGE_160;
	}
	else if (fc_enc == PG_WIN1250)
	{
		/*
		 * Window CP1250 <范围：128 -- 255>
		 */
		fc_ascii = (const unsigned char *) "  ' \"    %S<STZZ `'\"\".--  s>stzz   L A  \"CS  -RZ  ,l'u .,as L\"lzRAAAALCCCEEEEIIDDNNOOOOxRUUUUYTBraaaalccceeeeiiddnnoooo/ruuuuyt ";
		fc_range = RANGE_128;
	}
	else
	{
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("encoding conversion from %s to ASCII not supported",
						pg_encoding_to_char(fc_enc))));
		return;					/* 保持编译器安静 */
	}

	/*
	 * 编码
	 */
	for (fc_x = fc_src; fc_x < fc_src_end; fc_x++)
	{
		if (*fc_x < 128)
			*fc_dest++ = *fc_x;
		else if (*fc_x < fc_range)
			*fc_dest++ = ' ';		/* 虚假 128 到 '范围' */
		else
			*fc_dest++ = fc_ascii[*fc_x - fc_range];
	}
}

/* ----------
 * 编码文本
 *
 * 文本数据就地被覆盖，因此此编码方法
 * 不能支持改变字符串长度的转换！
 * ----------
 */
static text * fc_encode_to_ascii(text *fc_data, int fc_enc)
{
	pg_to_ascii((unsigned char *) VARDATA(fc_data),	/* src */
				(unsigned char *) (fc_data) + VARSIZE(fc_data),	/* src end */
				(unsigned char *) VARDATA(fc_data),	/* dest */
				fc_enc);			/* 编码 */

	return fc_data;
}

/* ----------
 * 转换为 ASCII - 编码设置为 'name' 参数。
 * ----------
 */
Datum to_ascii_encname(PG_FUNCTION_ARGS)
{
	text	   *fc_data = PG_GETARG_TEXT_P_COPY(0);
	char	   *fc_encname = NameStr(*PG_GETARG_NAME(1));
	int			fc_enc = pg_char_to_encoding(fc_encname);

	if (fc_enc < 0)
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("%s is not a valid encoding name", fc_encname)));

	PG_RETURN_TEXT_P(fc_encode_to_ascii(fc_data, fc_enc));
}

/* ----------
 * 转换为 ASCII - 编码设置为 int4
 * ----------
 */
Datum to_ascii_enc(PG_FUNCTION_ARGS)
{
	text	   *fc_data = PG_GETARG_TEXT_P_COPY(0);
	int			fc_enc = PG_GETARG_INT32(1);

	if (!PG_VALID_ENCODING(fc_enc))
		ereport(ERROR,
				(errcode(ERRCODE_UNDEFINED_OBJECT),
				 errmsg("%d is not a valid encoding code", fc_enc)));

	PG_RETURN_TEXT_P(fc_encode_to_ascii(fc_data, fc_enc));
}

/* ----------
 * 转换为 ASCII - 当前编码为 DatabaseEncoding
 * ----------
 */
Datum to_ascii_default(PG_FUNCTION_ARGS)
{
	text	   *fc_data = PG_GETARG_TEXT_P_COPY(0);
	int			fc_enc = GetDatabaseEncoding();

	PG_RETURN_TEXT_P(fc_encode_to_ascii(fc_data, fc_enc));
}

/* ----------
 * 在任意后端安全编码中复制字符串，转换为
 * 有效的 ASCII 字符串，并用 '?' 替换非 ASCII 字节。否则，行为与 strlcpy() 相同，
 * 除了我们不关心返回值。
 *
 * 这不能触发 ereport(ERROR)，因为它是在主进程中调用的。
 * ----------
 */
void ascii_safe_strlcpy(char *fc_dest, const char *fc_src, size_t fc_destsiz)
{
	if (fc_destsiz == 0)			/* 边角案例：没有空间放置结尾的 nul */
		return;

	while (--fc_destsiz > 0)
	{
		/* 在这里使用无符号字符以避免编译器警告 */
		unsigned char fc_ch = *fc_src++;

		if (fc_ch == '\0')
			break;
		/* 保留可打印的 ASCII 字符 */
		if (32 <= fc_ch && fc_ch <= 127)
			*fc_dest = fc_ch;
		/* 白空格也是可以的 */
		else if (fc_ch == '\n' || fc_ch == '\r' || fc_ch == '\t')
			*fc_dest = fc_ch;
		/* 所有其他内容都用 '?' 替换 */
		else
			*fc_dest = '?';
		fc_dest++;
	}

	*fc_dest = '\0';
}
